/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2018-11-08     balanceTWK   first version
 * 2024-06-01     yifengling0  for atomthreads
 */

#include "hw.h"
#include "pin.h"
#include "debug.h"
#include "simI2c.h"
#include "syslog.h"

/**
 * This function initializes the i2c pin.
 *
 * @param Stm32 i2c dirver class.
 */
static void sim_i2c_gpio_init(SimI2cConfig* cfg)
{
    PinMode(cfg->scl, PIN_MODE_OUTPUT_OD);
    PinMode(cfg->sda, PIN_MODE_OUTPUT_OD);

    PinWrite(cfg->scl, PIN_HIGH);
    PinWrite(cfg->sda, PIN_HIGH);
}

/**
 * This function sets the sda pin.
 *
 * @param Stm32 config class.
 * @param The sda pin state.
 */
static void sim_set_sda(void* data, int32_t state)
{
    SimI2cConfig* cfg = (SimI2cConfig*)data;

    SYSLOG(LOG_DEBUG, "sim_set_sda(%d, %d)", cfg->sda, state);

    if (state)
    {
        PinWrite(cfg->sda, PIN_HIGH);
    }
    else
    {
        PinWrite(cfg->sda, PIN_LOW);
    }
}

/**
 * This function sets the scl pin.
 *
 * @param Stm32 config class.
 * @param The scl pin state.
 */
static void  sim_set_scl(void* data, int32_t state)
{
    SimI2cConfig* cfg = (SimI2cConfig*)data;

    SYSLOG(LOG_DEBUG, "sim_set_scl(%d, %d)", cfg->scl, state);

    if (state)
    {
        PinWrite(cfg->scl, PIN_HIGH);
    }
    else
    {
        PinWrite(cfg->scl, PIN_LOW);
    }
}

/**
 * This function gets the sda pin state.
 *
 * @param The sda pin state.
 */
static int32_t sim_get_sda(void* data)
{
    SimI2cConfig* cfg = (SimI2cConfig*)data;

    int32_t ret = PinRead(cfg->sda);

    SYSLOG(LOG_DEBUG, "sim_get_sda(%d) = %d", cfg->sda, ret);

    return ret;
}

/**
 * This function gets the scl pin state.
 *
 * @param The scl pin state.
 */
static int32_t sim_get_scl(void* data)
{
    SimI2cConfig* cfg = (SimI2cConfig*)data;

    int32_t ret = PinRead(cfg->scl);

    SYSLOG(LOG_DEBUG, "sim_get_scl(%d) = %d", cfg->scl, ret);

    return ret;
}
/**
 * The time delay function.
 *
 * @param microseconds.
 */
static void sim_udelay(uint32_t us)
{
    HwDelayUs(us);
}

static I2cBitOps sim_bit_ops_default =
{
    .data = NULL,
    .set_sda = sim_set_sda,
    .set_scl = sim_set_scl,
    .get_sda = sim_get_sda,
    .get_scl = sim_get_scl,
    .udelay = sim_udelay,
    .delay_us = 1,
    .timeout = 100
};

/**
 * if i2c is locked, this function will unlock it
 *
 * @param stm32 config class
 *
 * @return EOK indicates successful unlock.
 */
static int sim_i2c_bus_unlock(const SimI2cConfig* cfg)
{
    int32_t i = 0;

    if (PIN_LOW == PinRead(cfg->sda))
    {
        while (i++ < 9)
        {
            PinWrite(cfg->scl, PIN_HIGH);
            sim_udelay(100);
            PinWrite(cfg->scl, PIN_LOW);
            sim_udelay(100);
        }
    }
    if (PIN_LOW == PinRead(cfg->sda))
    {
        return NG;
    }

    return OK;
}

/* I2C initialization function */
int SimI2cCreate(SimI2cConfig *config, int num)
{
    int result = ATOM_OK;

    for (int i = 0; i < num; i++)
    {
        sim_bit_ops_default.data = &config[i];
        config[i].ops = &sim_bit_ops_default;
        sim_i2c_gpio_init(&config[i]);
        result = sim_i2c_bus_unlock(&config[i]);
        if( result != ATOM_OK ){
            SYSLOG(LOG_ERR, "i2c bus unlock failure!");
        }
        I2cBitCreate(&config[i].owner, config[i].bus_name, config[i].ops);
        SYSLOG(LOG_INFO,"software simulation %s init done, pin scl: %d, pin sda %d", config[i].bus_name, config[i].scl, config[i].sda);
    }

    return result;
}
